"""This module contains various brains that can be used to control ships"""
import pygame, sys
import math, vecmath, game, net
from random import *

class AiController(game.Controller):
    """Base ai class, just sits and waits to be destroyed"""

class Erratic(AiController):
    """Simulates erratic periodical keystrokes like a player
    triying to figure out the controls"""
    def __init__(self, ship):
        AiController.__init__(self, ship)
        self.next_action_change = 0
        self.actions = [0] * 6
        self.methods = [self.ship.accelerate, self.ship.deccelerate,\
                        self.ship.left, self.ship.right, self.ship.fire_prim,\
                        self.ship.fire_sec] # cache the methods
        
    def control(self, delta):# perform random actions periodically
        if self.next_action_change < pygame.time.get_ticks(): #if due
            self.next_action_change = pygame.time.get_ticks() + randint(0, 500)
            self.actions[randint(0,5)] = randint(0, 1) # change one keystroke each time
        
        # map is faster than iteration
        map(self.do_action, self.actions, self.methods)
    
    def do_action(self, do, action):
        """action is a function object due to happen"""
        if do:
            action.__call__(game.delta)
    
    def onhit(self, bullet, delta = 1):# craze back to the offender
        self.actions = [0] * 6
        dx, dy = bullet.owner.position
        ox, oy = self.ship.position
        bearing = (math.degrees(math.atan2(dy - oy, dx - ox)) - self.ship.heading) % 360
        if bearing < 180:
            self.actions[self._action_map['left']] = 1
        else:
            self.actions[self._action_map['right']] = 1
        self.actions[self._action_map['prim']] = 1
        self.next_action_change = pygame.time.get_ticks() + 1500
        
class Simple(AiController):
    """Simulates a simple (but efficient) mind. Locks on an objective of another
    team and tracks it, turning, accelerating and shooting it if posible
    
    Changes the objective if kills the current or if is hit by another ship"""
    _action_map = {'acc':0, 'dec':1, 'left':2, 'right':3, 'prim':4, 'sec':5}
    def __init__(self, ship):
        AiController.__init__(self, ship);
        self.target = pygame.sprite.GroupSingle()
        self.actions = [0] * 6
        self.methods = [self.ship.accelerate, self.ship.deccelerate,\
                        self.ship.left, self.ship.right, self.ship.fire_prim,\
                        self.ship.fire_sec]

    def onhit(self, bullet):# target the offender or taunt the friend
        if(bullet.owner.__module__ != self.ship.__module__):
            if(bullet.owner.health > 0):
                self.target.add(bullet.owner)
        else:
            #game.debug(bullet.owner.__str__() + ', watch your fraking fire!!')
            pass
            
    def select_nearest(self):
        """Selects the nearest ship to this one"""
        sp = self.ship.position
        sm = self.ship.__module__
        df = vecmath.sqdist
        nd = [sys.maxint]
        ns = [None]
        def check_dist(sprite):#function to map
            if sprite.__module__ != sm :
                d = df(sp, sprite.position)
                if d > 0 and (nd[0] == 0 or nd[0] > d):
                    nd[0] = d
                    ns[0] = sprite
            
        map(check_dist, game.ships.sprites()) # mapping is faster than iterating
        return ns[0]

    def control(self, delta):
        self.actions = [0] * 6 # clear the actions
        t = self.target
        if not len(t) or t.sprite.health <= 0: # if untargeted
            n = self.select_nearest()
            if n:
                t.add(n)
        else:
            p = t.sprite.position
            sp = self.ship.position
            b = math.degrees(math.atan2(sp[1] - p[1], sp[0] - p[0])) - self.ship.heading
            b = (b + 180)%360
            d = vecmath.dist(p, sp)
            
            if((b < 2 or b > 358) and d < 800): # esta mas o menos delante
                self.actions[4] = 1 #shoot
            if d > 400:
                self.actions[0] = 1 #accelerate
                
            if(b > 180): # esta a la izquierda
                self.actions[2] = 1 #left
            else: # esta a la derecha
                self.actions[3] = 1 #right
                
            if d > 1000 or (b > 90 and b < 270):
                self.actions[5] = 1
            
            map(self.do_action, self.actions, self.methods)
    
    def do_action(self, do, action):
        """action is a function object due to happen"""
        if do:
            action.__call__(game.delta)
